package com.zzyl.job;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.zzyl.constant.CacheConstants;
import com.zzyl.dto.AlertNotifyDto;
import com.zzyl.entity.AlertData;
import com.zzyl.entity.AlertRule;
import com.zzyl.mapper.AlertDataMapper;
import com.zzyl.mapper.AlertRuleMapper;
import com.zzyl.mapper.DeviceMapper;
import com.zzyl.mapper.UserRoleMapper;
import com.zzyl.vo.DeviceDataVo;
import com.zzyl.websocket.WebSocketServer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 设备上报数据报警规则过滤
 */
@Slf4j
@Component
public class AlertJob {

    @Resource
    private AlertRuleMapper alertRuleMapper;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private DeviceMapper deviceMapper;
    @Resource
    private UserRoleMapper userRoleMapper;
    @Resource
    private AlertDataMapper alertDataMapper;
    @Resource
    private WebSocketServer webSocketServer;

    @Scheduled(cron = "0/10 * * * * ? ")
    //第一层逻辑，从
    public void deviceDataAlertFilter() {

//        1.查询所有报警规则，没有就返回
        List<AlertRule> alertRuleList = alertRuleMapper.selectAll();
        if (CollUtil.isEmpty(alertRuleList)) {
            return;
        }

//        2.从Redis拿到最新的数据（一分钟之内），没有就返回
        List<Object> objects = stringRedisTemplate.opsForHash().values(CacheConstants.IOT_DEVICE_LAST_DATA);
        if (CollUtil.isEmpty(objects)) {
            return;
        }

//        3.集合内容Object转AlertData
        ArrayList<DeviceDataVo> bigDeviceDataList = new ArrayList<>();
        for (Object obj : objects) {
            List<DeviceDataVo> deviceDataVos = JSON.parseArray(obj.toString(), DeviceDataVo.class);
            bigDeviceDataList.addAll(deviceDataVos);
        }


//        4.遍历大集合，查对应报警规则
        for (DeviceDataVo deviceDataVo : bigDeviceDataList) {
            alertFilter(deviceDataVo);
        }

    }

    //第二层逻辑
    public void alertFilter(DeviceDataVo deviceDataVo) {
//        1.拿一分钟之内的数据，没有就返回
        LocalDateTime alarmTime = deviceDataVo.getAlarmTime();      //上报时间
        LocalDateTime current = LocalDateTime.now();                //当前时间
        Duration between = Duration.between(alarmTime, current);    //相隔时间
        if (between.getSeconds() > 60) {
            return;
        }

//        2.找出对应的报警规则，没有就返回
        List<AlertRule> alertRuleList = alertRuleMapper.selectByFunctionId(deviceDataVo.getProductKey(),
                deviceDataVo.getIotId(), deviceDataVo.getFunctionId());
        if (CollUtil.isEmpty(alertRuleList)) {
            return;
        }

//        3.有数据，有规则，将数据遍历规则就行
        for (AlertRule alertRule : alertRuleList) {
            deviceDateAlarmHandler(alertRule, deviceDataVo);
        }

    }

    //第三层逻辑
    public void deviceDateAlarmHandler(AlertRule alertRule, DeviceDataVo deviceDataVo) {
//        1.上报时间不在规则生效时间内，直接返回
        LocalTime startTime = LocalTime.parse(alertRule.getAlertEffectivePeriod().split("~")[0]);
        LocalTime endTime = LocalTime.parse(alertRule.getAlertEffectivePeriod().split("~")[1]);
        LocalTime alarmTime = deviceDataVo.getAlarmTime().toLocalTime();
        if (alarmTime.isBefore(startTime) || alarmTime.isAfter(endTime)) {
            return;
        }

//        2.没达到阈值，校验结束（为了实现连续多次才报警，所有有一次好的数据就删除Redis数据）
        String alertTriggerCountRedisKey = CacheConstants.ALERT_TRIGGER_COUNT + deviceDataVo.getIotId() + ":" + alertRule.getId();
        int compareResult = Double.compare(Double.parseDouble(deviceDataVo.getDataValue()), Double.valueOf(alertRule.getValue()));
        if (compareResult >= 0 && alertRule.getOperator().equals(">=") || compareResult < 0 && alertRule.getOperator().equals("<")){
        }else {
            stringRedisTemplate.delete(alertTriggerCountRedisKey);
            return;
        }

//        3.如果有沉默周期，校验结束
        String alertSilentCycleRedisKey = CacheConstants.ALERT_SILENT_CYCLE + deviceDataVo.getIotId() + ":" + alertRule.getId();
        if(stringRedisTemplate.hasKey(alertSilentCycleRedisKey)){
            return;
        }

//        4.不在校验持续周期，校验结束
        Long alertTriggerCount = stringRedisTemplate.opsForValue().increment(alertTriggerCountRedisKey, 1);
        if(!NumberUtil.equals(alertTriggerCount,alertRule.getDuration())){
            return;
        }
//      ////////////////////////////////////////  5.所有条件满足，报警
//      ////////////////////////////////////////  5.所有条件满足，报警
//      ////////////////////////////////////////  5.所有条件满足，报警

//        1.添加沉默周期
        stringRedisTemplate.opsForValue().set(alertSilentCycleRedisKey, "true", alertRule.getAlertSilentPeriod(), TimeUnit.MINUTES);

//        2.删除持续周期
        stringRedisTemplate.delete(alertTriggerCountRedisKey);

//        3.不同地方的设备分别找护理员/维修工，超级管理员
        List<Long> userIds = new ArrayList<>();
        if(deviceDataVo.getPhysicalLocationType()==-1){                 //老人
            userIds = deviceMapper.selectNursingIdsByIotIdWithElder(deviceDataVo.getIotId());
        }
        if(deviceDataVo.getPhysicalLocationType()==2){                 //床位
            userIds = deviceMapper.selectNursingIdsByIotIdWithBed(deviceDataVo.getIotId());
        }
        if(deviceDataVo.getPhysicalLocationType()==1){                  //房间
            userIds = userRoleMapper.selectUserIdsByRoleName("护理员");
        }
        List<Long> adminIds = userRoleMapper.selectUserIdsByRoleName("超级管理员");
        List<Long> allUserIds = CollUtil.addAllIfNotContains(userIds, adminIds);

//        4.给需要的全部用户增加报警数据
        insertAlertData(deviceDataVo,alertRule,allUserIds);

//        5.发送WebSocket请求给前端
        AlertNotifyDto alertNotifyDto = new AlertNotifyDto();
        BeanUtil.copyProperties(alertRule, alertNotifyDto);
        BeanUtil.copyProperties(deviceDataVo,alertNotifyDto);
        alertNotifyDto.setVoiceNotifyStatus(1);    //语言通知状态  0：关闭 1：开启
        alertNotifyDto.setNotifyType(1);           //通知类型  0：接触报警  1：报警
        alertNotifyDto.setIsAllConsumer(false);    //是否通知所有人
        webSocketServer.sendMessageToConsumer(alertNotifyDto,allUserIds);
    }

    private void insertAlertData(DeviceDataVo deviceDataVo, AlertRule alertRule, List<Long> consumerIds) {

        String alertReason = StrUtil.format("{} {} {}, 持续 {} 个周期就报警",
                alertRule.getFunctionName(),
                alertRule.getOperator(),
                alertRule.getValue(),
                alertRule.getDuration());


        AlertData alertData = new AlertData();
        BeanUtil.copyProperties(deviceDataVo,alertData,"createTime","updatedTime","createBy","updateBy");
        BeanUtil.copyProperties(alertRule,alertData,"createTime","updatedTime","createBy","updateBy");

        alertData.setAlertRuleId(alertRule.getId());// 报警规则id
        alertData.setAlertReason(alertReason);// 报警原因，格式：功能名称+运算符+阈值+持续周期+聚合周期
        alertData.setType(alertRule.getAlertDataType());//报警数据类型，0：老人异常数据，1：设备异常数据
        alertData.setStatus(0);// 状态，0：待处理，1：已处理

        List<AlertData> list = consumerIds.stream().map(id -> {
            AlertData dbAlertData = BeanUtil.toBean(alertData, AlertData.class);
            dbAlertData.setUserId(id);// 接收者id
            return dbAlertData;
        }).collect(Collectors.toList());

        alertDataMapper.batchInsert(list);
    }
}